#include "header.h"
#include "net.h"
#include "extern.h"
#include "macros.h"
#include "linkedlists.h"
#include "hashtables.h"
#include "critical.h"
#include "channels.h"

int Send (int sock, const char buffer[BUFFERSIZE]){

	return (send (sock, buffer, strlen(buffer), 0));
}

char * gethost (struct sockaddr_in ss){

 struct hostent* hs = NULL;
 char *ptr = NULL;

	hs = gethostbyaddr (&ss.sin_addr.s_addr, sizeof(ss.sin_addr.s_addr), AF_INET);
		if (hs != NULL){
			ptr = hs->h_name;
		}
		else
			ptr = inet_ntoa (ss.sin_addr);;

 return (ptr);
}

int serv_socket_create (int domain, int type, struct sockaddr_in* sin)
{
 int sock,
	 gard,
	 val;
 socklen_t servlen;

	sock = socket (domain, type, 0);
		if (sock == -1){
			perror("socket()");
			return -1;
		}

	val = 1;
	gard = setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, (char*)&val, sizeof (val));
		if (gard == -1){
			fprintf (stderr, "SO_REUSEADDR option not set\n");
		}

	servlen = sizeof (*sin);
	gard = bind (sock, (struct sockaddr *)sin, servlen);
		if (gard == -1){
			perror ("bind()");
			return -1;
		}

	gard = listen (sock, NetworkConf.MaxQueuLen);
		if (gard == -1){
			perror ("listen()");
			return -1;
		}

 return sock;
}
#ifdef USE_SSL
int serv_socket_ssl_create (int domain, int type, struct sockaddr_in* sin)
{
 int sock,
	 gard,
	 val;
 socklen_t servlen;

	SSL_library_init();
	SSL_load_error_strings();
	serv_ssl_meth = SSLv3_method();
	serv_ssl_ctx = SSL_CTX_new (serv_ssl_meth);
	if (!serv_ssl_ctx){
		fprintf (stderr, "unable to create ssl context. Disabling ssl support...\n");
		SSL_CTX_free(serv_ssl_ctx);
		return -1;
	}

	sock = socket (domain, type, 0);
		if (sock == -1){
			perror("socket()");
			return -1;
		}

	val = 1;
	gard = setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, (char*)&val, sizeof (val));
		if (gard == -1){
			fprintf (stderr, "SO_REUSEADDR option not set\n");
		}

	servlen = sizeof (*sin);
	gard = bind (sock, (struct sockaddr *)sin, servlen);
		if (gard == -1){
			perror ("Zbind()");
			return -1;
		}

	gard = listen (sock, NetworkConf.MaxQueuLen);
		if (gard == -1){
			perror ("listen()");
			return -1;
		}

 return sock;
}
#endif

void UpdateMaxFD (Thread* thread)
/*
	
*/
{
	int max = 0;
	Client* id = NULL;

		thread->MaxFds = 0;
		LIST_FOREACH(id, thread->ids_list){
			if (id->Socket.Socket != -1)
				thread->MaxFds = MAX(max, id->Socket.Socket);
		}
}

struct sockaddr_in* NetInit (int family, unsigned int cport)
{

 struct sockaddr_in *ss = NULL;

	ss = malloc (sizeof (struct sockaddr_in));
		if (ss == NULL)
		{
			printf ("no enough memory\n");/*<--------------------------------------------------*/
		}
		else
		{
			memset (ss, 0, sizeof (ss));
			ss->sin_family = family;
			ss->sin_port = htons (cport);
			ss->sin_addr.s_addr = INADDR_ANY;
		}

	return (ss);
}

int SendToClient (Client* client, const char *format, ...)
/*
	
*/
{
 va_list ap;
 char buffer[BUFFERSIZE];
 int gard;

 	va_start (ap, format);
	vsnprintf (buffer, sizeof (buffer), format, ap);
	va_end (ap);

	MUTEX_LOCK (client->Socket.mutex);
	gard = send (client->Socket.Socket, buffer, strlen (buffer), 0);
	MUTEX_UNLOCK (client->Socket.mutex);
		if (gard == -1){
			perror("send()");
			return -1;
		}
		if (gard == 0){/*broken pipe*/
			printf ("send():SIGPIPE\n");
			return -2;
		}
	/* RunHooks (HOOK_ON_SEND, id); see for passing the buffer*/

 return 0;
}

void SendToChan (Channel* channel, const char *format, ...)
/*
	
*/
{
 va_list ap;
 int gard;
 char buffer[BUFFERSIZE];
 ChannelClient *chclient = NULL;
 Client* client = NULL;

 	va_start (ap, format);
	vsnprintf (buffer, sizeof (buffer), format, ap);
	va_end (ap);

	LIST_FOREACH (chclient, channel->clients)
	{
		client = HashTable_Find (clientstb, chclient->user);
		if (client != NULL)
		{
			gard = SendToClient (client, buffer);
			if (gard == -1)
			{
				KillClient (client->user, "Send error");
			}
			else
			if (gard == -1)
			{
				KillClient (client->user, "Broken pipe");
			}
		}
	}

}

int RecvFromUser(int Socket, char* buffer, int size)
/*
	
*/
{
 int gard = 0, totalrcv = 0;

	while ((totalrcv < size)&&(buffer[totalrcv-1] != '\n')){
		gard = recv (Socket, buffer + totalrcv, size, 0/*MSG_DONTWAIT*/);
			if (gard == 0){/*broken pipe*/
				printf ("recvFromUser(): SIGPIPE\n");
				return -2;
			}
			if (gard == -1){
				perror("recv()");
				return -1;
			}
		totalrcv = totalrcv + gard;
	}

	if (totalrcv <= size){
		buffer[totalrcv-1] = '\0';
		/* RunHooks (HOOK_ON_RECEIVE, id) see for passing buffer */
	}
	else
		gard = -1;

 return gard;
}

void CloseSocket (SecSockets* sock){
	MUTEX_LOCK (sock->mutex);
	shutdown (sock->Socket, SHUT_RDWR);
	close (sock->Socket);
	sock->Socket = -1;
	MUTEX_UNLOCK (sock->mutex);
}


void KillClient (const char* user, const char *reason)
/*
	
*/
{
  Client* client = NULL;
  ClientChannel *clchannel = NULL;
  Channel* channel = NULL;

	client = HashTable_Find (clientstb, user);
	if (client != NULL)
	{
		CloseSocket (&client->Socket);
		LIST_FOREACH (clchannel, client->channels){
			channel = HashTable_Find (channelstb, clchannel->name);
			if (channel != NULL)
			{
				chan_member_del (channel, client->user);
				SendToChan (channel, "KILL %s %s\r\n", client->user, reason);
			}
		}
	}
	free (client);
	/* RunHooks(HOOK_ON_KILL, suser); */
}

